#include <stdlib.h>
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include <tlhelp32.h>
#include <time.h>
#include "winmine.h"

#define MAKEDWORD(h, l) ((h << 16) | l)
#define PATH "%SystemRoot%\\system32\\winmine.exe"
#define INITIAL_SLEEP_TIME 250
#define PVMINEFIELD_S 0x01005361
#define PVMINEFIELD_E 0x0100567f
#define PVHEIGHT	  0x10056a8
#define PVWIDTH		  0x10056ac
#define PVNMINES	  0x10056a4
#define MINEFLAG	  0xffffff8f
#define	MINEMARKED	  0xffffff8e

DWORD dwHeight;
DWORD dwWidth;
DWORD nMines;

BOOL ReadSettingsFromMemory(HANDLE hProcess)
{
	DWORD dwSuccess;
	DWORD nBytesRead;
	dwSuccess = 
		ReadProcessMemory(hProcess,				// handle to the process
						  (LPCVOID)PVHEIGHT,	// base address
						  &dwHeight,			// buffer output
						  sizeof(DWORD),		// size of buff
						  &nBytesRead);			// num bytes read
	if (dwSuccess == 0)
		return FALSE;
	
	dwSuccess = 
		ReadProcessMemory(hProcess,				// handle to the process
						  (LPCVOID)PVWIDTH,		// base address
						  &dwWidth,				// buffer output
						  sizeof(DWORD),		// size of buff
						  &nBytesRead);			// num bytes read
	if (dwSuccess == 0)
		return FALSE;

	dwSuccess = 
		ReadProcessMemory(hProcess,				// handle to the process
						  (LPCVOID)PVNMINES,    // base address
						  &nMines,				// buffer output
						  sizeof(DWORD),		// size of buff
						  &nBytesRead);			// num bytes read
	if (dwSuccess == 0)
		return FALSE;
	
	
	return TRUE;
}

DWORD StartMinesweeper()
{
	// Get minesweeper path
	TCHAR szWinminePath[160];
	DWORD dwSuccess;
	dwSuccess = 
		ExpandEnvironmentStrings(_T(PATH),
								 szWinminePath,
								 _countof(szWinminePath));
	if (dwSuccess == 0) 
		return -1;

	STARTUPINFO startinfoWinmine;
	memset(&startinfoWinmine, 0, sizeof(startinfoWinmine));
	startinfoWinmine.cb = sizeof(startinfoWinmine);
	
	PROCESS_INFORMATION procinfoWinmine;
	dwSuccess =
		CreateProcess(szWinminePath,		// application name
					  0,					// command line
					  0,					// process attributes
					  0,					// thread attributes
					  0,					// inherit handles
					  0,					// creation flags
					  0,					// environment
					  0,					// current directory
					  &startinfoWinmine,	// startup info,
					  &procinfoWinmine);	// process info
	if (dwSuccess == 0)
		_ftprintf(stderr, _T("Couldn't start Minesweeper.\n"));
	
	return procinfoWinmine.dwProcessId;
}

BOOL bWriteOneByte(HANDLE hProcess, LPVOID lpAddress, char byte)
{
	DWORD dwSuccess;
	DWORD nBytesRead;
	dwSuccess = WriteProcessMemory(hProcess,				// process handle
								   lpAddress,				// base address
								   &byte,					// buffer input
					               1,	// size of buff
								   &nBytesRead);			// num bytes read
	return dwSuccess;
}
			
BOOL bReadOneByte(HANDLE hProcess, LPCVOID lpAddress, char * byte)
{
	DWORD dwSuccess;
	DWORD nBytesRead;
	dwSuccess = 
		ReadProcessMemory(hProcess,				// handle to the process
						  lpAddress,			// base address
						  byte,					// buffer output
						  1,// size of buff
						  &nBytesRead);			// num bytes read
	return dwSuccess;
}

char ReadPosition(HANDLE hProcess, int x, int y)
{
	LPBYTE addr = (LPBYTE)0x01005361 + y*32 +x;
	char cMineValue;
	bReadOneByte(hProcess, addr, &cMineValue);
	return cMineValue;
}

BOOL bWritePosition(HANDLE hProcess, int x, int y, char cMineValue)
{
	LPBYTE addr = (LPBYTE)0x01005361 + y*32 +x;
	return bWriteOneByte(hProcess, addr, cMineValue);
}

BOOL bSetCursorMinePos(HWND hWnd, int x, int y)
{
	RECT rectWindow;
	DWORD dwSuccess = GetWindowRect(hWnd, &rectWindow);
	if (dwSuccess == NULL)
		return FALSE;
	y = rectWindow.top + 106 + y*16;
	x = rectWindow.left + 25 + x*16;
	SetCursorPos(x,y);
	return TRUE;
}

void ClickRandomMine(HANDLE hProcess, HWND hWnd, BOOL bRightClick)
{
	int x, y;
	do {
		x = rand() % dwWidth;
		y = rand() % dwHeight;
	}
	while (ReadPosition(hProcess, x, y) != 0xf);
	ClickMine(hWnd, x, y, bRightClick);
}

void ClickMine(HWND hWnd, int x, int y, BOOL bRightClick)
{
	bSetCursorMinePos(hWnd, x, y);
	SetForegroundWindow(hWnd);
	x = 19 + x*16;
	y = 63 + y*16;
	
	if (bRightClick) 
	{
		SendMessage(hWnd, WM_RBUTTONDOWN, NULL, MAKEDWORD(y,x));
		SendMessage(hWnd, WM_RBUTTONUP, NULL, MAKEDWORD(y,x));
	}
	else
	{
		SendMessage(hWnd, WM_LBUTTONDOWN, NULL, MAKEDWORD(y,x));
		SendMessage(hWnd, WM_LBUTTONUP, NULL, MAKEDWORD(y,x));
	}
}

DWORD GetPid(TCHAR ProcessName[])
{
	HANDLE hSnapshot;
	hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
	if (hSnapshot == INVALID_HANDLE_VALUE)
		return -1;

	PROCESSENTRY32 procEntry32;
	procEntry32.dwSize = sizeof(procEntry32);
	if (FALSE == Process32First(hSnapshot, &procEntry32))
	{
		CloseHandle(hSnapshot);
		return -1;
	}

	do
	{
		if (_tcsicmp(ProcessName, procEntry32.szExeFile) == 0)
		{
			CloseHandle(hSnapshot);
			return procEntry32.th32ProcessID;
		}
	} while (Process32Next(hSnapshot, &procEntry32));
	
	CloseHandle(hSnapshot);
	return -1;
}

int nTypeMinesSurrounding(HANDLE hProcess, int x, int y, int flag)
{
	int nMines = 0;
	// top left position
	if (ReadPosition(hProcess, x-1, y-1) == flag)
		nMines++;
	// directly above
	if (ReadPosition(hProcess, x, y-1) == flag)
		nMines++;

	// top right position
	if (ReadPosition(hProcess, x+1, y-1) == flag)
		nMines++;

	//  right position
	if (ReadPosition(hProcess, x+1, y) == flag)
		nMines++;

	//  bottom right position
	if (ReadPosition(hProcess, x+1, y+1) == flag)
		nMines++;

	//  bottom position
	if (ReadPosition(hProcess, x, y+1) == flag)
		nMines++;

	//  bottom left position
	if (ReadPosition(hProcess, x-1, y+1) == flag)
		nMines++;

	//  left position
	if (ReadPosition(hProcess, x-1, y) == flag)
		nMines++;
	return nMines;
}


BOOL bNForN(HANDLE hProcess, int x, int y)
{
	int nOpen = 0;
	for (int i = x-1; i <= x+1; i++)
		for (int j = y-1; j <= y+1; j++)
		{
			if (ReadPosition(hProcess, i, j) == 0x0f)
				return FALSE;
		}
	return TRUE;
}

BOOL bMarkMines(HANDLE hProcess, HWND hWnd, int * nMines)
{
	BOOL bRetVal = FALSE;
	for (int j = 0; j < dwHeight; j++)
		for (int i = 0; i < dwWidth; i++)
		{			
			char cMineValue;
			cMineValue = ReadPosition(hProcess, i, j);
			if (cMineValue <= 0x48 && cMineValue >= 0x40 && bNForN(hProcess, i, j))
			{
				for (int x = i-1; x <= i+1; x++)
					for (int y = j-1; y <= j+1; y++)
					{
						cMineValue = ReadPosition(hProcess, x, y);
						if (cMineValue == MINEFLAG)
						{
							ClickMine(hWnd, x, y, TRUE);
							*nMines = *nMines - 1;
							bRetVal = TRUE;
							Sleep(1);
						}
					}
				
			}
			else if (nTypeMinesSurrounding(hProcess, i, j, MINEMARKED) == (cMineValue - 0x40))
			{
				for (int x = i-1; x <= i+1; x++)
					for (int y = j-1; y <= j+1; y++)
					{
						cMineValue = ReadPosition(hProcess, x, y);
						if (cMineValue == 0x0f)
						{
							ClickMine(hWnd, x, y, FALSE);
							bRetVal = TRUE;
							Sleep(1);
						}
					}
			}
		}
	return bRetVal;
}

int nMinesWithProperty(HANDLE hProcess, int FLAG)
{
	int count = 0;
	for (int y = 0; y < dwHeight; y++)
		for (int x = 0; x < dwWidth; x++)
		{
			if (ReadPosition(hProcess, x, y) == FLAG)
				count++;
		}
	return count;
}

int _tmain(int argc, wchar_t* argv[])
{
	srand(time(NULL));
	DWORD dwProcIdWinmine;
	dwProcIdWinmine = GetPid(_T("winmine.exe"));
	if (dwProcIdWinmine == -1)
	{
		_ftprintf(stderr, _T("Couldn't find open instance of minesweeper.\n"));
		_tprintf(_T("Trying to start minesweeper...\n"));
		dwProcIdWinmine = StartMinesweeper();
		if (dwProcIdWinmine == -1)
		{
			_ftprintf(stderr, _T("Couldn't start minesweeper.\n"));
			return 1;
		}
	}

	// Get a handle to Minesweeper
	HANDLE hWinmine;
	hWinmine = OpenProcess(PROCESS_ALL_ACCESS,
						   FALSE,
						   dwProcIdWinmine);
	if (hWinmine == NULL)
	{
		_ftprintf(stderr, _T("Couldn't get a handle to Minesweeper.\n"));
		return 1;
	}

	
	Sleep(INITIAL_SLEEP_TIME);
	if (!ReadSettingsFromMemory(hWinmine))
	{
		_ftprintf(stderr, _T("Couldn't read width and height from memory.\n"));
		CloseHandle(hWinmine);
		return 1;
	}

	_tprintf(_T("Width:\t\t%d\n"), dwWidth);
	_tprintf(_T("Height:\t\t%d\n"), dwHeight);
	_tprintf(_T("Num mines:\t%d\n"), nMines);
	
	// Get window properties
	HWND hMinesweeper = FindWindow(_T("Minesweeper"), _T("Minesweeper"));
	if (hMinesweeper == NULL)
	{
		_ftprintf(stderr, _T("Couldn't get a handle to the window.\n"));
		CloseHandle(hWinmine);
		return 1;
	}
	
	int nMinesLeft = nMines; 
	while (nMinesWithProperty(hWinmine, 0x0f))
	{
		if(!bMarkMines(hWinmine, hMinesweeper, &nMinesLeft))
		{
			ClickRandomMine(hWinmine, hMinesweeper, FALSE);
			Sleep(1);
		}
		_tprintf(_T("nMinesLeft: %d\n"), nMinesLeft);
	}
	Sleep(1);
	/*int nMinesLeft = nMines;
	for (int j = 0; j < dwHeight; j++)
		for (int i = 0; i < dwWidth; i++){
			bSetCursorMinePos(hMinesweeper, i, j);			
			char cMineValue;
			cMineValue = ReadPosition(hWinmine, i, j);
			if (cMineValue != MINEFLAG)
				ClickMine(hMinesweeper, i, j, FALSE);
			else
			{
				nMines--;
				ClickMine(hMinesweeper, i, j, TRUE);
			}
			Sleep(1);
		}*/

	CloseHandle(hWinmine);
	return 0;
}